package com.way.demo.filter;

import cn.hutool.core.exceptions.ExceptionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.util.*;

/**
 * @author lipingwei
 * @create 2021-01-25 15:04
 */
@Slf4j
public class ReaderReuseHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] contentData = null;

    private Map<String,String[]> paramsMap= new HashMap<>();
    private HttpServletRequest req;

    public ReaderReuseHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        req = request;
        initContentData(request);
        this.paramsMap.putAll(request.getParameterMap());
    }
    public ReaderReuseHttpServletRequestWrapper(HttpServletRequest request,Map<String,Object> extendParams) {
        super(request);
        req = request;
        initContentData(request);
        this.addAllParamters(extendParams);
    }

    private void initContentData(HttpServletRequest request){
        log.info("请求方法为：=== {}",request.getMethod());
        try(ByteArrayOutputStream outputStream =new ByteArrayOutputStream()){
            StreamUtils.copy(request.getInputStream(),outputStream);
            contentData=  outputStream.toByteArray();
        }catch (IOException ex){
            log.error("copy request body from inputstream error: {}", ExceptionUtil.getMessage(ex));
            if(contentData==null){
                contentData=new byte[0];
            }
        }
    }


    private void addAllParamters(Map<String,Object> otherParams){
        Set<Map.Entry<String, Object>> entrySet = otherParams.entrySet();
        for(Map.Entry<String, Object> entry : entrySet){
            String key = entry.getKey();
            Object value = entry.getValue();
            setParamsMap(key,value);
        }
    }

    private void setParamsMap(String key, Object value){
        if(null != value){
            if(value instanceof String[]){
                paramsMap.put(key,(String[])value);
            }else if(value instanceof String){
                paramsMap.put(key,new String[]{(String)value});
            }else {
                paramsMap.put(key,new String[]{String.valueOf(value)});
            }
        }
    }

    @Override
    public Enumeration<String> getParameterNames() {
      List<String> ls=  new ArrayList<>(paramsMap.keySet());
        Iterator<String> iterator = ls.iterator();
        return new Enumeration<String>(){
            @Override
            public boolean hasMoreElements(){ return iterator.hasNext();}
            @Override
             public String nextElement(){ return iterator.next();}
        };
    }

    @Override
    public String getParameter(String name) {
        String[] value = paramsMap.get(name);
        if(null == value || value.length==0){
            return null;
        }
        return value[0];
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(contentData);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }

        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        String encoding = req.getCharacterEncoding();
        if(null==encoding){
            encoding="UTF-8";
        }
        return  new BufferedReader(new InputStreamReader(new ByteArrayInputStream(contentData),encoding));
    }

    @Override
    public String[] getParameterValues(String name) {
        return paramsMap.get(name);
    }
}
